﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LuceneWrap.Lib;
using System.IO;
using Lucene.Net.Index;
using Lucene.Net.QueryParsers;
using NewsMine.Utilities;
using NewsMine.Storage.SQLite;

namespace NewsMine.Storage
{
    /// <summary>
    /// todo:Enhance this by using the SQLite, instead of having the lucene index. 
    /// </summary>

    public class KeyValueStoreOnFileSystem : IKeyValueStore
    {

        bool isStoredOpened = false;
        LuceneManager<KeyValueItem> luceneManager;
        LuceneSearcher searcher;
        string storeDirectory;

        QueueOnFileSystem addingItemsQueue;
        QueueOnFileSystem replacingItemsQueue;

        object syncObject = new object();


        public KeyValueStoreOnFileSystem(string _storeDirectory)
        {
            lock (syncObject)
            {
                storeDirectory = _storeDirectory;

                string addQueuePath = Path.Combine(_storeDirectory, "AddQueue");
                string replaceQueuePath = Path.Combine(_storeDirectory, "ReplaceItemsQueue");

                if (!Directory.Exists(addQueuePath))
                    Directory.CreateDirectory(addQueuePath);

                if (!Directory.Exists(replaceQueuePath))
                    Directory.CreateDirectory(replaceQueuePath);

                addingItemsQueue = new QueueOnFileSystem(addQueuePath);
                replacingItemsQueue = new QueueOnFileSystem(replaceQueuePath);

                //sqliteKeyValueHelper = new SQLiteKeyValueHelper(Path.Combine(storeDirectory, "sqliteKVItems.db"));
            }
        }

        bool allowDuplicates = false;

        public void Set(string key, object obj)
        {

            lock (syncObject)
            {
                if (allowDuplicates == false)
                {
                    Replace(key, obj);
                    return;
                }

                if (isStoredOpened == false)
                    Open();

                //if the store is locked, then queue the items.
                if (isStoredOpened == false)
                {
                    addingItemsQueue.Enqueue(new KeyValueItem { Key = key, Value = new ValueItem { Value = obj } });
                    return;
                }

                //consider adding the stuff in next visit.
                AddQueuedItemsFromAddQueue();


                //AddItemToSQLiteKeyValueDB(new KeyValueItem { Key = key, Value = new ValueItem { Value = obj } });

                luceneManager.AddItemToIndex(new KeyValueItem { Key = key, Value = new ValueItem { Value = obj } });

                luceneManager.IndexWriter.Commit();
            }
        }

        public void SetMultiple(Dictionary<string, object> keyValueDictionary)
        {
            lock (syncObject)
            {
                if (allowDuplicates == false)
                    ReplaceMultiple(keyValueDictionary);

                var result = new List<KeyValueItem>();

                foreach (var k in keyValueDictionary.Keys)
                {
                    result.Add(new KeyValueItem { Key = k, Value = new ValueItem { Value = keyValueDictionary[k] } });
                }

                if (isStoredOpened == false)
                    Open();

                //if the store is locked, then queue the items.
                if (isStoredOpened == false)
                {
                    foreach (var kvItem in result)
                    {
                        addingItemsQueue.Enqueue(kvItem);
                    }

                    return;
                }

                //consider adding the stuff in next visit.

                AddQueuedItemsFromAddQueue();

                //AddItemsToSQLiteKeyValueDB(result);

                luceneManager.AddItemsToIndex(result);

                luceneManager.IndexWriter.Commit();
            }
        }

        private void AddQueuedItemsFromAddQueue()
        {
            lock (syncObject)
            {
                if (addingItemsQueue.GetCount() > 0)
                {
                    while (true)
                    {
                        object objFromQueue = addingItemsQueue.Dequeue();
                        if (objFromQueue == null)
                            break;

                        KeyValueItem kvItem = (KeyValueItem)objFromQueue;

                        //AddItemToSQLiteKeyValueDB(kvItem);
                        luceneManager.AddItemToIndex(kvItem);
                    }
                }
            }
        }

        public object Get(string key)
        {
            lock (syncObject)
            {
                //var value = GetKeyValueItemFromSqliteByKey(key);

                //if (value != null)
                //    return value;


                MakeSureIndexSearcherIsOpened();


                if (searcher == null)
                    return null;

                var result = searcher.SearchByPrimaryKeyField<KeyValueItem>("key", key);
                //var result = searcher.GetResults<KeyValueItem>("key:" + "\"" + key + "\"");

                if (result != null && result.Count > 0)
                {

                    return result[0].Value.Value;
                }
                else
                    return null;
            }
        }

        

        public List<object> Get(int startIndex, int length)
        {
            lock (syncObject)
            {
                MakeSureIndexSearcherIsOpened();

                if (searcher == null)
                    return null;

                var result = searcher.GetItemsByIndexRange<KeyValueItem>(startIndex, length);
                List<object> objects = new List<object>();

                if (result != null && result.Count > 0)
                {
                    foreach (var item in result)
                    {
                        objects.Add(item.Value.Value);
                    }
                }

                return objects;
            }
        }

        private void MakeSureIndexSearcherIsOpened()
        {
            try
            {
                if (searcher == null)
                    searcher = new LuceneSearcher(storeDirectory);
                else
                {
                    try
                    {
                        if (searcher.IndexSearcher.GetIndexReader().IsCurrent() == false)
                        {
                            searcher.Finalize();
                            searcher = new LuceneSearcher(storeDirectory);
                        }
                    }
                    catch (Lucene.Net.Store.AlreadyClosedException ex)
                    {
                        //todo:logging needs tobe done here.
                        //Sometimes the search will be closed, this time we need to reopen.
                        //hope the below code should work.

                        searcher = new LuceneSearcher(storeDirectory);

                    }
                }
            }
            catch (System.IO.FileNotFoundException ioException)
            {
                if (ioException.Message.ToLower().Contains("no segments* file found"))
                {
                    Open();
                    Close();
                    if (searcher == null)
                        searcher = new LuceneSearcher(storeDirectory);
                }
            }
        }

        public int GetCount()
        {
            MakeSureIndexSearcherIsOpened();

            if (searcher != null)
                return searcher.MaxDoc();
            else
                return 0;
        }

    

        public void Replace(string key, object obj)
        {
            if (isStoredOpened == false)
                Open();

            //if the store is locked, then queue the items.
            if (isStoredOpened == false)
            {
                replacingItemsQueue.Enqueue(new KeyValueItem { Key = key, Value = new ValueItem { Value = obj } });
                return;
            }

            //consider adding the stuff in next visit.
            AddQueuedItemsFromReplacingQueue();

            MakeSureIndexSearcherIsOpened();

            var result = searcher.SearchByPrimaryKeyField<KeyValueItem>("key", key);

            if (result.Count > 0)
            {
                luceneManager.DeleteObjectFromIndexByField("key", key);
                //luceneManager.DeleteObjectFromIndexByField("Key", key);
            }

            //Term termToUpdateDoc = new Term("key", key.ToLower());

           // AddItemToSQLiteKeyValueDB(new KeyValueItem { Key = key, Value = new ValueItem { Value = obj } });

            luceneManager.AddItemToIndex(new KeyValueItem { Key = key, Value = new ValueItem { Value = obj } });
        }

        private void AddQueuedItemsFromReplacingQueue()
        {
            while (true)
            {
                object objToReplace = replacingItemsQueue.Dequeue();

                if (objToReplace == null)
                    break;

                if (!(objToReplace is KeyValueItem))
                    continue;

                KeyValueItem kvItem = (KeyValueItem)objToReplace;

                MakeSureIndexSearcherIsOpened();

                var result = searcher.SearchByPrimaryKeyField<KeyValueItem>("key:", kvItem.Key);

                if (result.Count > 0)
                {
                    luceneManager.DeleteObjectFromIndexByQuery("key:" + "\"" + kvItem.Key + "\"");
                }

                //Term termToUpdateDoc = new Term("key", key.ToLower());

               // AddItemToSQLiteKeyValueDB(kvItem);
                luceneManager.AddItemToIndex(kvItem);
            }
        }

        public void ReplaceMultiple(Dictionary<string, object> keyValueDictionary)
        {
            foreach (var key in keyValueDictionary.Keys)
            {
                Replace(key, keyValueDictionary[key]);
            }
        }

        public void Delete(string key)
        {
            if (isStoredOpened == false)
                Open();

            luceneManager.DeleteObjectFromIndexByField("key", key);
            //DeleteItemFromSQLiteDB(key);
            Close();
        }



        public void Open()
        {
            try
            {
                if (luceneManager != null && luceneManager.IndexWriter != null)
                {
                    luceneManager.IndexWriter.Commit();
                    luceneManager.IndexWriter.Close(true);
                }

                luceneManager = new LuceneWrap.Lib.LuceneManager<KeyValueItem>(storeDirectory);
                MakeSureIndexSearcherIsOpened();

                isStoredOpened = true;
            }
            catch (Exception e)
            {
                TempLogger.Log("Error opening Lucene manager inside KeyValueStoreOnFileSystem", e);
                isStoredOpened = false;
            }
        }

        public void Close()
        {
            if (isStoredOpened == false)
                return;

            AddQueuedItemsFromAddQueue();
            AddQueuedItemsFromReplacingQueue();

            searcher.Finalize();
            luceneManager.FinalizeWriter(true);
            searcher = null;
            luceneManager = null;

            isStoredOpened = false;
        }

     

       

        #region SQLITE key value stuff
        ////SQLiteKeyValueHelper sqliteKeyValueHelper;

        ////todo: now
        //private void AddItemToSQLiteKeyValueDB(KeyValueItem keyValueItem)
        //{
        //    sqliteKeyValueHelper.Set(keyValueItem.Key, ObjectByteArrayConverter.ObjectToByteArray(keyValueItem));
        //}

        ////todo: now
        //private void AddItemsToSQLiteKeyValueDB(List<KeyValueItem> keyValueItems)
        //{
        //    foreach (var item in keyValueItems)
        //    {
        //        AddItemToSQLiteKeyValueDB(item);
        //    }
        //}

        //private object GetKeyValueItemFromSqliteByKey(string key)
        //{
        //    var data = sqliteKeyValueHelper.Get(key);

        //    if (data == null)
        //        return null;

        //    KeyValueItem kvItem = (KeyValueItem)ObjectByteArrayConverter.ByteArrayToObject(data);

        //    return kvItem.Value.Value;

        //}

        //private void DeleteItemFromSQLiteDB(string key)
        //{
        //    sqliteKeyValueHelper.DeleteByKey(key);
        //}

        #endregion


        public bool IsKeyExists(string key)
        {
            throw new NotImplementedException();
        }


        public object Get(string key, bool forceLatest)
        {
            throw new NotImplementedException();
        }
    }
}
